// 
// SoapTrace.Net - A soap message monitoring utility. 
// 
// Copyright (C) 2004 Matthew Ward
// 
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
// 
// Matthew Ward (mrward@users.sourceforge.net)
// 

using System;
using System.Diagnostics;
using WinPcap;

namespace SoapTrace.Core.Communication
{
	/// <summary>
	/// Monitors soap messages either through a proxy or via
	/// packet capture.
	/// </summary>
	public class SoapMessageMonitor : IDisposable
	{
		/// <summary>
		/// Disposed flag.
		/// </summary>
		private bool m_Disposed;
		
		/// <summary>
		/// Remote server's IP address.
		/// </summary>
		private string m_RemoteServerIPAddress = "localhost";

		/// <summary>
		/// Remote server's port.
		/// </summary>
		private int m_RemoteServerPort = 80;

		/// <summary>
		/// Listening port for the soap trace proxy.
		/// </summary>
		private int m_ProxyServerListeningPort = 8080;

		/// <summary>
		/// A status flag indicating whether we are using a proxy server.
		/// </summary>
		private bool m_UseProxyServer = true;

		/// <summary>
		/// A status flag indicating whether we are using winpcap packet capture.
		/// </summary>
		private bool m_UseWinPcap;
		
		/// <summary>
		/// A status flag indicating whether we are using raw sockets packet capture.
		/// </summary>
		private bool m_UseRawSockets;		
		
		/// <summary>
		/// The name of the network interface to use for packet capture.
		/// </summary>
		private string m_NetworkInterfaceName = String.Empty;
		
		/// <summary>
		/// Flag indicating whether we will capture tcp packets when
		/// we missed the initial connection.
		/// </summary>
		private bool m_MissedConnectionCaptureEnabled;

		/// <summary>
		/// Proxy server.
		/// </summary>
		private SoapMessageProxyServer m_ProxyServer;
		
		/// <summary>
		/// The packet monitor.
		/// </summary>
		private SoapPacketMonitor m_PacketMonitor;

		/// <summary>
		/// Soap request received event.
		/// </summary>
		public event SoapRequestMessageReceivedEventHandler SoapRequestMessageReceived;

		/// <summary>
		/// Soap response received event.
		/// </summary>
		public event SoapResponseMessageReceivedEventHandler SoapResponseMessageReceived;

		/// <summary>
		/// Packet received event.
		/// </summary>
		/// <remarks>This event will only be triggered if 
		/// WinPcap is being used.
		/// </remarks>
		public event PacketReceivedEventHandler PacketReceived;
	
		/// <summary>
		/// IP Packet received event.
		/// </summary>
		/// <remarks>
		/// This event will only be triggered if raw sockets is
		/// being used.
		/// </remarks>
		public event PacketReceivedEventHandler IPPacketReceived;
		
		/// <summary>
		/// Creates a new instance of the <see cref="SoapMessageMonitor"/>
		/// class.
		/// </summary>
		public SoapMessageMonitor( )
		{
		}

		/// <summary>
		/// Disposes this class.
		/// </summary>
		public void Dispose( )
		{
			Dispose( true );

			GC.SuppressFinalize( this );
		}
		
		/// <summary>
		/// Starts the message monitor.
		/// </summary>
		public void Start( )
		{
			if( UseProxyServer )
			{
				StartProxyServer( );
			}
			else
			{
				StartPacketMonitor( );
			}
		}

		/// <summary>
		/// Stops the message monitor.
		/// </summary>
		public void Stop( )
		{
			StopProxyServer( );
			StopPacketMonitor( );
		}

		/// <summary>
		/// Gets or sets the remote server's IP address.
		/// </summary>
		public string RemoteServerIPAddress
		{
			[ DebuggerStepThrough ]
			get
			{
				return m_RemoteServerIPAddress;
			}

			[ DebuggerStepThrough ]
			set
			{
				m_RemoteServerIPAddress = value;
			}
		}
		
		/// <summary>
		/// Gets or sets the remote server's port.
		/// </summary>
		public int RemoteServerPort
		{
			[ DebuggerStepThrough ]
			get
			{
				return m_RemoteServerPort;
			}

			[ DebuggerStepThrough ]
			set
			{
				m_RemoteServerPort = value;
			}
		}

		/// <summary>
		/// Gets the proxy server's port.
		/// </summary>
		public int ProxyServerListeningPort
		{
			[ DebuggerStepThrough ]
			get
			{
				return m_ProxyServerListeningPort;
			}

			[ DebuggerStepThrough ]
			set
			{
				m_ProxyServerListeningPort = value;
			}
		}

		/// <summary>
		/// Gets or sets whether the soap trace application should use a 
		/// proxy server for message monitoring.
		/// </summary>
		public bool UseProxyServer
		{
			[ DebuggerStepThrough ]
			get
			{
				return m_UseProxyServer;
			}

			[ DebuggerStepThrough ]
			set
			{
				m_UseProxyServer = value;
			}
		}

		/// <summary>
		/// Gets or sets whether the soap trace application should use 
		/// winpcap packet capture for message monitoring.
		/// </summary>
		public bool UseWinPcap
		{
			[ DebuggerStepThrough ]
			get
			{
				return m_UseWinPcap;
			}

			[ DebuggerStepThrough ]
			set
			{
				m_UseWinPcap = value;
				if( value )
				{
					CreatePacketMonitor( );
					m_PacketMonitor.UseWinPcap = true;
				}
			}
		}
		
		/// <summary>
		/// Gets or sets whether tcp packets will be captured even
		/// if we missed the initial connection.
		/// </summary>
		public bool MissedConnectionCaptureEnabled
		{
			get
			{
				return m_MissedConnectionCaptureEnabled;
			}
			
			set
			{
				m_MissedConnectionCaptureEnabled = value;
				
				if( m_PacketMonitor != null )
				{
					m_PacketMonitor.MissedConnectionCaptureEnabled = value;
				}
			}
		}
		
		/// <summary>
		/// Gets or sets whether the soap trace application should use 
		/// raw sockets packet capture for message monitoring.
		/// </summary>
		public bool UseRawSockets
		{
			[ DebuggerStepThrough ]
			get
			{
				return m_UseRawSockets;
			}

			[ DebuggerStepThrough ]
			set
			{
				m_UseRawSockets = value;
				
				if( value )
				{
					CreatePacketMonitor( );
					m_PacketMonitor.UseWinPcap = false;
				}
			}
		}

		/// <summary>
		/// Gets or sets the network interface name to use
		/// when capturing packets.
		/// </summary>
		public string NetworkInterfaceName 
		{
			get 
			{
				return m_NetworkInterfaceName;
			}
			set 
			{
				m_NetworkInterfaceName = value;
			}
		}
		
		/// <summary>
		/// Gets the available network interfaces as determined from Windows
		/// Raw sockets.
		/// </summary>
		/// <returns>A collection of network interface names.</returns>
		public static NetworkInterfaceNameCollection GetRawSocketNetworkInterfaceNames( )
		{			
			return SoapPacketMonitor.GetRawSocketNetworkInterfaceNames( );
		}
		
		/// <summary>
		/// Gets the available network interfaces as determined from WinPcap.
		/// </summary>
		/// <returns>A collection of network interface names.</returns>
		public static NetworkInterfaceNameCollection GetWinPcapNetworkInterfaceNames( )
		{
			return SoapPacketMonitor.GetWinPcapNetworkInterfaceNames( );
		}
		
		/// <summary>
		/// Stops the proxy server.
		/// </summary>
		private void StopProxyServer( )
		{
			if( m_ProxyServer != null )
			{
				m_ProxyServer.Stop( );
			}
		}

		/// <summary>
		/// Starts the proxy server.
		/// </summary>
		private void StartProxyServer( )
		{
			if( m_ProxyServer == null )
			{
				m_ProxyServer = new SoapMessageProxyServer( );

				m_ProxyServer.SoapRequestReceived += 
					new SoapRequestMessageReceivedEventHandler( OnSoapRequestReceived );

				m_ProxyServer.SoapResponseReceived += 
					new SoapResponseMessageReceivedEventHandler( OnSoapResponseReceived );
			}

			m_ProxyServer.Start( m_ProxyServerListeningPort, 
				m_RemoteServerIPAddress, 
				m_RemoteServerPort );
		}

		/// <summary>
		/// Soap message response received.
		/// </summary>
		/// <param name="sender">The event source.</param>
		/// <param name="e">A <see cref="SoapResponseReceivedEventArgs"/> object.</param>
		private void OnSoapResponseReceived( object sender, SoapResponseReceivedEventArgs e )
		{
			if( SoapResponseMessageReceived != null )
			{
				SoapResponseMessageReceived( sender, e );
			}		
		}

		/// <summary>
		/// Soap message request received.
		/// </summary>
		/// <param name="sender">The event source.</param>
		/// <param name="e">A <see cref="SoapRequestReceivedEventArgs"/> object.</param>
		private void OnSoapRequestReceived( object sender, SoapRequestReceivedEventArgs e )
		{
			if( SoapRequestMessageReceived != null )
			{
				SoapRequestMessageReceived( sender, e );
			}
		}
		
		/// <summary>
		/// Packet received.
		/// </summary>
		/// <param name="sender">The event source.</param>
		/// <param name="e">A <see cref="PacketReceivedEventArgs"/> object.</param>
		private void OnPacketReceived( object sender, PacketReceivedEventArgs e )
		{
			if( PacketReceived != null )
			{
				PacketReceived( this, e );
			}
		}		
		
		/// <summary>
		/// IP Packet received.
		/// </summary>
		/// <param name="sender">The event source.</param>
		/// <param name="e">A <see cref="PacketReceivedEventArgs"/> object.</param>
		private void OnIPPacketReceived( object sender, PacketReceivedEventArgs e )
		{
			if( IPPacketReceived != null )
			{
				IPPacketReceived( this, e );
			}
		}			
		
		/// <summary>
		/// Starts the packet monitor that will capture data packets.
		/// </summary>
		private void StartPacketMonitor( )
		{
			CreatePacketMonitor( );
			
			m_PacketMonitor.UseWinPcap = UseWinPcap;
			
			m_PacketMonitor.Start( m_NetworkInterfaceName, 
			                      m_RemoteServerIPAddress,
			                      m_RemoteServerPort );
		}
		
		/// <summary>
		/// Stops the packet monitor.
		/// </summary>
		private void StopPacketMonitor( )
		{
			if( m_PacketMonitor != null )
			{
				m_PacketMonitor.Stop( );
			}
		}
		
		/// <summary>
		/// Creates a new packet monitor if we have not done so already
		/// and hooks up the soap request/response received events.
		/// </summary>
		private void CreatePacketMonitor( )
		{
			if( m_PacketMonitor == null )
			{
				m_PacketMonitor = new SoapPacketMonitor( );
				
				m_PacketMonitor.MissedConnectionCaptureEnabled = m_MissedConnectionCaptureEnabled;
				
				m_PacketMonitor.SoapRequestMessageReceived += 
					new SoapRequestMessageReceivedEventHandler( OnSoapRequestReceived );

				m_PacketMonitor.SoapResponseMessageReceived += 
					new SoapResponseMessageReceivedEventHandler( OnSoapResponseReceived );				
			
				m_PacketMonitor.PacketReceived += 
					new PacketReceivedEventHandler( OnPacketReceived );
				
				m_PacketMonitor.IPPacketReceived +=
					new PacketReceivedEventHandler( OnIPPacketReceived );
			}
		}
		
		/// <summary>
		/// Disposes managed and unmanaged objects. 
		/// </summary>
		/// <param name="disposing">If <see langword="true"/> the method has been called directly
		/// or indirectly by a user's code. Managed and unmanaged resources
		/// can be disposed.
		/// If disposing equals <see langword="false"/>, the method has been called by the 
		/// runtime from inside the finalizer and you should not reference 
		/// other objects. Only unmanaged resources can be disposed.
		/// </param>
		private void Dispose( bool disposing )
		{
			if( !m_Disposed )
			{
				if( disposing )
				{
					// Dispose managed resources.
					Stop( );
				}
			}

			m_Disposed = true;         
		}		
	}
}
